<?php
/*
 * @Description: 微信支付类
 * @Author: QianLong
 * @Date: 2019-09-27 16:34:02
 * @LastEditors    : QianLong
 * @LastEditTime   : 2021-05-08 17:24:57
 */

namespace app\common\service;

use Yurun\Util\HttpRequest;
use app\common\service\wxpay\Config;
use app\lib\exception\ApiException;

class Wxpay extends Config
{
    private $wxUrl;
    public function __construct()
    {
        parent::__construct();
        $this->wxUrl = 'https://api.mch.weixin.qq.com/';
    }
    /**
     * JsApi支付
     * @param array $orderData
     * @param string $notify_url
     * @return void
     * @author QianLong <87498106@qq.com>
     * @date 2021-04-06 17:56:13
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2021-04-06 17:56:13
     */
    public function payForJsapi(array $orderData, string $notify_url = null)
    {
        $url = $this->wxUrl.'v3/pay/transactions/jsapi';
        if (!isset($orderData['openid']) || empty($orderData['openid'])) {
            throw new ApiException("缺少微信用户OpenId");
        }
        $postData = $this->buildOrderData($orderData, $notify_url);
        $token = $this->getAuthorization($url,$postData);
        $http = new HttpRequest;
        $response = $http->header('Authorization',$token)->post($url,$postData,'json');
        $result = json_decode($response->body(), true);
        if (isset($result['code_url']) && !empty($result['code_url'])) {
            return ['code'=>200,'data'=>$result['code_url']];
        }else{
            return ['code'=>-1,'data'=>$result];
        }
    }
    /**
     * APP支付
     * @param array $orderData
     * @param string $notify_url
     * @return void
     * @author QianLong <87498106@qq.com>
     * @date 2021-04-07 15:31:41
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2021-04-07 15:31:41
     */
    public function payForApp(array $orderData, string $notify_url = null)
    {
        $url = $this->wxUrl.'v3/pay/transactions/app';
        $postData = $this->buildOrderData($orderData, $notify_url);
        $token = $this->getAuthorization($url,$postData);
        $http = new HttpRequest;
        $response = $http->header('Authorization',$token)->post($url,$postData,'json');
        $result = json_decode($response->body(), true);
        if (isset($result['prepay_id']) && !empty($result['prepay_id'])) {
            $return['appid'] = $this->infoData['app_id'];
            $return['partnerid'] = $this->infoData['mch_id'];
            $return['prepayid'] = $result['prepay_id'];
            $return['package'] = 'Sign=WXPay';
            $return['noncestr'] = createId();
            $return['timestamp'] = time();
            $return['sign'] = $this->generalSign($return['appid']."\n".$return['timestamp']."\n".$return['noncestr']."\n".$return['prepayid']."\n");
            return ['code'=>200,'data'=>$return];
        }else{
            return ['code'=>-1,'data'=>$result];
        }
    }
    /**
     * 二维码支付
     * @param array $orderData
     * @param string $notify_url
     * @return void
     * @author QianLong <87498106@qq.com>
     * @date 2021-04-06 15:46:13
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2021-04-06 15:46:13
     */
    public function payForQrcode(array $orderData, string $notify_url = null)
    {
        $url = $this->wxUrl.'v3/pay/transactions/native';
        $postData = $this->buildOrderData($orderData, $notify_url);
        $token = $this->getAuthorization($url,$postData);
        $http = new HttpRequest;
        $response = $http->header('Authorization',$token)->post($url,$postData,'json');
        $result = json_decode($response->body(), true);
        if (isset($result['code_url']) && !empty($result['code_url'])) {
            return ['code'=>200,'data'=>$result['code_url']];
        }else{
            return ['code'=>-1,'data'=>$result];
        }
    }
    /**
     * 企业付款
     * @param array $orderData
     * @return void
     * @author QianLong <87498106@qq.com>
     * @date 2021-05-07 17:14:03
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2021-05-07 17:14:03
     */
    public function mchPay(array $orderData)
    {
        $url = $this->wxUrl.'mmpaymkttransfers/promotion/transfers';
        $postData = $this->buildMchPayData($orderData);
        $token = $this->getAuthorization($url,$postData);
        $http = new HttpRequest;
        $response = $http->header('Authorization',$token)->post($url,$postData,'json');
        $result = json_decode($response->body(), true);
        if (isset($result['result_code']) && !empty($result['result_code']) && strtoupper($result['result_code']) == 'SUCCESS') {
            return ['code'=>200,'data'=>$result];
        }else{
            return ['code'=>-1,'data'=>$result];
        }
    }
    /**
     * 退款
     * @param array $orderData
     * @return void
     * @author QianLong <87498106@qq.com>
     * @date 2021-05-07 17:32:32
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2021-05-07 17:32:32
     * @doc https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_9.shtml
     */
    public function refund(array $orderData)
    {
        $url = $this->wxUrl.'v3/refund/domestic/refunds';
        $postData = $this->buildRefundData($orderData);
        $token = $this->getAuthorization($url,$postData);
        $http = new HttpRequest;
        $response = $http->header('Authorization',$token)->post($url,$postData,'json');
        $result = json_decode($response->body(), true);
        if (isset($result['refund_id']) && !empty($result['refund_id'])) {
            return ['code'=>200,'data'=>$result];
        }else{
            return ['code'=>-1,'data'=>$result];
        }
    }
    /**
     * 构建订单数据（统一下单）
     * @param array $orderData
     * @param string $notify_url
     * @return void
     * @author QianLong <87498106@qq.com>
     * @date 2021-04-05 14:46:17
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2021-04-05 14:46:17
     */
    private function buildOrderData(array $orderData = null, $notify_url = ''): array
    {
        if (!isset($orderData['out_trade_no']) || empty($orderData['out_trade_no'])) {
            throw new ApiException("缺少商户单号");
        }
        if (!isset($orderData['total_amount']) || empty($orderData['total_amount'])) {
            throw new ApiException("缺少金额");
        }
        if (!isset($orderData['subject']) || empty($orderData['subject'])) {
            throw new ApiException("缺少订单标题");
        }
        $postData['appid'] = $this->infoData['app_id'];
        $postData['mchid'] = $this->infoData['mch_id'];
        $postData['description'] = $orderData['subject'];
        $postData['out_trade_no'] = $orderData['out_trade_no'];
        if (isset($orderData['attach']) && !empty($orderData['attach'])) {
            $postData['attach'] = $orderData['attach'];
        }
        if (isset($orderData['time_expire']) && !empty($orderData['time_expire'])) {
            $postData['time_expire'] = $orderData['time_expire'];
        }
        $postData['notify_url'] = $notify_url;
        if (empty($postData['notify_url'])) {
            $postData['notify_url'] = $this->infoData['notify_url'];
        }
        if (!empty($orderData['openid'])) {
            $postData['payer']['openid'] = $orderData['openid'];
        }
        $postData['amount']['total'] = intval($orderData['total_amount'] * 100);
        if (isset($orderData['invoice_id']) && !empty($orderData['invoice_id'])) {
            $postData['detail']['invoice_id'] = $orderData['invoice_id'];
        }
        $postData['scene_info']['payer_client_ip'] = get_client_ip();
        return $postData;
    }
    /**
     * 构建企业付款数据
     * @param array $orderData
     * @return array
     * @author QianLong <87498106@qq.com>
     * @date 2021-05-07 17:15:31
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2021-05-07 17:15:31
     */
    private function buildMchPayData(array $orderData): array
    {
        if (!isset($orderData['out_trade_no']) || empty($orderData['out_trade_no'])) {
            throw new ApiException("缺少商户单号 out_trade_no", 1);
        }
        if (!isset($orderData['openid']) || empty($orderData['openid'])) {
            throw new ApiException("缺少用户信息 openid", 1);
        }
        if (!isset($orderData['amount']) || empty($orderData['amount'])) {
            throw new ApiException("缺少金额 amount", 1);
        }
        if (!isset($orderData['desc']) || empty($orderData['desc'])) {
            throw new ApiException("缺少企业付款备注 desc", 1);
        }
        if (!isset($orderData['check_name']) || empty($orderData['check_name'])) {
            throw new ApiException("校验用户姓名选项 check_name（NO_CHECK：不校验真实姓名，FORCE_CHECK：强校验真实姓名）", 1);
        }
        $postData['mch_appid'] = $this->infoData['app_id'];
        $postData['mchid'] = $this->infoData['mch_id'];
        $postData['nonce_str'] = createId();
        $postData['desc'] = $orderData['desc'];
        $postData['check_name'] = $orderData['check_name'];
        $postData['amount'] = $orderData['amount']*100;
        $postData['partner_trade_no'] = $orderData['out_trade_no'];
        // 收款用户真实姓名
        if (isset($orderData['re_user_name']) && !empty($orderData['re_user_name'])) {
            $postData['re_user_name'] = $orderData['re_user_name'];
        }
        $postData['openid'] = $orderData['openid'];
        $postData['spbill_create_ip'] = get_client_ip();
        return $postData;
    }
    /**
     * 构建退款数据
     * @param array $orderData
     * @param string $notify_url
     * @return array
     * @author QianLong <87498106@qq.com>
     * @date 2021-05-07 17:33:11
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2021-05-07 17:33:11
     */
    private function buildRefundData(array $orderData = null, $notify_url = ''): array
    {
        // 原支付交易对应的微信订单号
        if (!isset($orderData['out_trade_no']) || empty($orderData['out_trade_no'])) {
            throw new ApiException("缺少三方平台订单号 out_trade_no", 1);
        }
        if (!isset($orderData['refund']) || empty($orderData['refund'])) {
            throw new ApiException("缺少退款金额 refund", 1);
        }
        if (!isset($orderData['total_amount']) || empty($orderData['total_amount'])) {
            throw new ApiException("缺少订单总金额 total_amount", 1);
        }
        // 商户系统内部的退款单号，商户系统内部唯一，只能是数字、大小写字母_-|*@ ，同一退款单号多次请求只退一笔。
        if (!isset($orderData['out_refund_no']) || empty($orderData['out_refund_no'])) {
            throw new ApiException("缺少退款单号 out_refund_no", 1);
        }
        // $postData['appid'] = $this->infoData['app_id'];
        // $postData['mchid'] = $this->infoData['mch_id'];
        $postData['amount']['refund'] = $orderData['refund']*100;
        $postData['amount']['total'] = $orderData['total_amount']*100;
        $postData['amount']['currency'] = 'CNY';
        $postData['transaction_id'] = $orderData['out_trade_no'];
        $postData['out_refund_no'] = $orderData['out_refund_no'];
        if (isset($orderData['reason']) && !empty($orderData['reason'])) {
            $postData['reason'] = $orderData['reason'];
        }
        $postData['notify_url'] = $notify_url;
        if (empty($postData['notify_url'])) {
            $postData['notify_url'] = $this->infoData['notify_url'];
        }
        if (!empty($orderData['openid'])) {
            $postData['payer']['openid'] = $orderData['openid'];
        }
        return $postData;
    }
}
